package com.gy.assistant.ui.meeting;

import android.app.AlertDialog;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import im.zego.zim.ZIM;
import im.zego.zim.callback.ZIMCallCancelSentCallback;
import im.zego.zim.callback.ZIMCallEndSentCallback;
import im.zego.zim.callback.ZIMCallInvitationSentCallback;
import im.zego.zim.callback.ZIMCallQuitSentCallback;
import im.zego.zim.callback.ZIMCallRejectionSentCallback;
import im.zego.zim.callback.ZIMCallingInvitationSentCallback;
import im.zego.zim.entity.ZIMCallAcceptConfig;
import im.zego.zim.entity.ZIMCallCancelConfig;
import im.zego.zim.entity.ZIMCallEndConfig;
import im.zego.zim.entity.ZIMCallEndedSentInfo;
import im.zego.zim.entity.ZIMCallInvitationEndedInfo;
import im.zego.zim.entity.ZIMCallInvitationReceivedInfo;
import im.zego.zim.entity.ZIMCallInvitationSentInfo;
import im.zego.zim.entity.ZIMCallInviteConfig;
import im.zego.zim.entity.ZIMCallQuitConfig;
import im.zego.zim.entity.ZIMCallQuitSentInfo;
import im.zego.zim.entity.ZIMCallRejectConfig;
import im.zego.zim.entity.ZIMCallUserInfo;
import im.zego.zim.entity.ZIMCallUserStateChangeInfo;
import im.zego.zim.entity.ZIMCallingInvitationSentInfo;
import im.zego.zim.entity.ZIMCallingInviteConfig;
import im.zego.zim.entity.ZIMError;
import im.zego.zim.entity.ZIMPushConfig;
import im.zego.zim.enums.ZIMErrorCode;
import com.gy.assistant.MyApplication;
import com.gy.assistant.R;
import com.gy.assistant.SDKManager;
import com.gy.assistant.ui.meeting.adapter.CallData;
import com.gy.assistant.util.PreferenceUtil;

public class CallManager implements SDKManager.OnCallUserStateChangedCallBack, SDKManager.OnCallInvitationEndedCallBack, SDKManager.OnCallInvitationReceivedCallback {
    private final List<CallDataChanged> callbackList = new ArrayList<>();
    private final HashMap<String, HashMap<String, CallData>> dataMap = new HashMap<>();
    private String currentCallID = "";
    private AlertDialog invitationDialog;

    private static String defaultResourceId = "";

    private static final class SdkManagerHolder {
        static final CallManager sdkManager = new CallManager();
    }

    public static CallManager sharedInstance() {
        defaultResourceId = PreferenceUtil.getInstance(MyApplication.context).getStringValue("resourceId","");
        return SdkManagerHolder.sdkManager;
    }

    public void init() {
        initEventHandler();
    }

    public void addMeetingDataChanged(CallDataChanged callback) {
        this.callbackList.add(callback);
    }

    public void removeMeetingDataChanged(CallDataChanged callback) {
        this.callbackList.remove(callback);
    }

    public ArrayList<CallData> getCurrentList(String callID) {
        HashMap<String , CallData> userMap = dataMap.get(callID);
        ArrayList<CallData> list;
        if (userMap!= null){
            list = new ArrayList<>(userMap.values());
        }else {
            list = new ArrayList<>();
        }
        return list;
    }

    private void initEventHandler() {
        SDKManager.sharedInstance().setCallUserStateChangedCallBack(this);
        SDKManager.sharedInstance().setCallInvitationReceivedCallback(this);
        SDKManager.sharedInstance().setCallInvitationEndedCallBack(this);
    }

    private void jumpToEndActivity(String callerID, long meetingDuration, long userDuration) {
        if (MyApplication.getCurrentActivity() != null) {
            MeetingEndedActivity.actionStart(MyApplication.getCurrentActivity(), callerID, meetingDuration, userDuration);
            if (MyApplication.getCurrentActivity() instanceof InMeetingActivity){
                MyApplication.getCurrentActivity().finish();
            }
        }
    }

    private void jumpToInMeetingActivity(String callerID) {
        if (MyApplication.getCurrentActivity() != null) {
            InMeetingActivity.actionStart(MyApplication.getCurrentActivity(), currentCallID, callerID);
        }
    }

    @Override
    public void onCallUserStateChanged(ZIM zim, ZIMCallUserStateChangeInfo info,String callID) {
        HashMap<String, CallData> map = dataMap.get(callID);
        if (map == null) {
            map = new HashMap<>();
        }

        for (ZIMCallUserInfo userInfo : info.callUserList) {
            CallData data = new CallData();
            data.setCallUserInfo(userInfo);
            map.put(userInfo.userID, data);
        }
        dataMap.put(callID, map);

        for (CallDataChanged callback : this.callbackList) {
            callback.onMeetingUserDataChangedCallback(new ArrayList<>(map.values()));
        }
    }

    public void callInvite(List<String> invitees, ZIMCallInviteConfig config, ZIMCallInvitationSentCallback callback) {
        ZIMPushConfig pushConfig = new ZIMPushConfig();
        pushConfig.resourcesID = defaultResourceId;
        pushConfig.content = "call Invite";
        pushConfig.title = MyApplication.sUserId;
        config.pushConfig = pushConfig;

        config.extendedData = "invite extendedData";
        SDKManager.sharedInstance().callInvite(invitees, config, new ZIMCallInvitationSentCallback() {
            @Override
            public void onCallInvitationSent(String callID, ZIMCallInvitationSentInfo info, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS){
                    currentCallID = callID;
                }
                if (callback != null) {
                    callback.onCallInvitationSent(callID, info, errorInfo);
                }
            }
        });
    }

    public void callEnd(String callID, ZIMCallEndConfig config, ZIMCallEndSentCallback callback) {
        config.extendedData = "call end extendedData";

        ZIMPushConfig pushConfig = new ZIMPushConfig();
        pushConfig.resourcesID = defaultResourceId;
        pushConfig.content = "call End";
        pushConfig.title = MyApplication.sUserId;
        config.pushConfig = pushConfig;
        SDKManager.sharedInstance().callEnd(callID, config, new ZIMCallEndSentCallback() {
            @Override
            public void onCallEndSent(String callID, ZIMCallEndedSentInfo info, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS){
                    currentCallID = callID;
                }
                if (callback != null) {
                    callback.onCallEndSent(callID, info, errorInfo);
                }
            }
        });
    }

    public void callQuit(String callID, ZIMCallQuitConfig config, ZIMCallQuitSentCallback callback) {
        config.extendedData = "call quit extendedData";

        ZIMPushConfig pushConfig = new ZIMPushConfig();
        pushConfig.resourcesID = defaultResourceId;
        pushConfig.content = "call Quit";
        pushConfig.title = MyApplication.sUserId;
        config.pushConfig = pushConfig;

        SDKManager.sharedInstance().callQuit(callID, config, new ZIMCallQuitSentCallback() {
            @Override
            public void onCallQuitSent(String callID, ZIMCallQuitSentInfo info, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS){
                    currentCallID = callID;
                }
                if (callback != null) {
                    callback.onCallQuitSent(callID, info, errorInfo);
                }
            }
        });
    }

    public void callCancel(List<String> invitees, String callID,ZIMCallCancelSentCallback callback){
        ZIMCallCancelConfig config = new ZIMCallCancelConfig();
        config.extendedData = "call cancel extendedData";
        SDKManager.sharedInstance().callCancel(invitees, callID,config,callback);
    }

    public void callingInvite(String callID, List<String> invitees, ZIMCallingInviteConfig config, ZIMCallingInvitationSentCallback callback) {
        ZIMPushConfig pushConfig = new ZIMPushConfig();
        pushConfig.resourcesID = defaultResourceId;
        pushConfig.content = "calling Invite";
        pushConfig.title = MyApplication.sUserId;
        config.pushConfig = pushConfig;
        SDKManager.sharedInstance().callingInvite(callID, invitees, config, new ZIMCallingInvitationSentCallback() {
            @Override
            public void onCallingInvitationSent(String callID, ZIMCallingInvitationSentInfo info, ZIMError errorInfo) {

                if (callback != null) {
                    callback.onCallingInvitationSent(callID, info, errorInfo);
                }
            }
        });
    }

    private void callAccept(String callID, ZIMCallInvitationReceivedInfo info) {
        ZIMCallAcceptConfig config = new ZIMCallAcceptConfig();
        config.extendedData = "call accept extendedData";

        SDKManager.sharedInstance().callAccept(callID,config, (callID1, errorInfo) -> {
            if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                currentCallID = callID;
                jumpToInMeetingActivity(info.caller);
                if (invitationDialog != null && invitationDialog.isShowing()) {
                    invitationDialog.dismiss();
                    invitationDialog = null;
                }
            }
        });
    }

    private void showInvitationDialog(String callID, String content, ZIMCallInvitationReceivedInfo info) {
        if (MyApplication.getCurrentActivity() == null || MyApplication.getCurrentActivity().isFinishing()) {
            return;
        }
        invitationDialog = new AlertDialog.Builder(MyApplication.getCurrentActivity())
                .setMessage(content)
                .setPositiveButton(R.string.accept, (dialog, which) -> callAccept(callID, info))
                .setNegativeButton(R.string.reject, (dialog, which) -> callReject(callID))
                .show();
    }

    private void callReject(String callID) {
        ZIMCallRejectConfig config = new ZIMCallRejectConfig();
        config.extendedData = "call reject extendedData";

        SDKManager.sharedInstance().callReject(callID,config, new ZIMCallRejectionSentCallback() {
            @Override
            public void onCallRejectionSent(String callID, ZIMError errorInfo) {
                if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                    dataMap.remove(callID);
                    if (invitationDialog != null && invitationDialog.isShowing()) {
                        invitationDialog.dismiss();
                        invitationDialog = null;
                    }
                }
            }
        });
    }

    @Override
    public void onCallInvitationReceived(ZIM zim, ZIMCallInvitationReceivedInfo info, String callID) {

        HashMap<String, CallData> map = dataMap.get(callID);
        if (map == null) {
            map = new HashMap<>();
        }
        for (ZIMCallUserInfo userInfo : info.callUserList) {
            CallData data = new CallData();
            data.setCallUserInfo(userInfo);
            data.setTimeout(info.timeout);
            map.put(userInfo.userID, data);
        }

        dataMap.put(callID, map);

        if (!currentCallID.isEmpty()) {
            return;
        }
        showInvitationDialog(callID, String.format("%s邀请你进行通话", info.inviter), info);
    }

    @Override
    public void onCallInvitationEnded(ZIM zim, ZIMCallInvitationEndedInfo info, String callID) {
        if (!callID.equals(currentCallID)) {
            return;
        }
        currentCallID = "";
        dataMap.clear();
        // Fixme 这里需要修改成两个不同时间戳的相减。但是目前SDK没有创建时间和接受时间。
        jumpToEndActivity(info.caller, info.endTime, info.endTime);
    }

    public void clear(){
        currentCallID = "";
        dataMap.clear();
        if (invitationDialog !=null && invitationDialog.isShowing()){
            invitationDialog.dismiss();
        }
        callbackList.clear();
    }

    abstract static class CallDataChanged {
        void onMeetingUserDataChangedCallback(ArrayList<CallData> meetingDataList) {

        }
    }

}
